package main

import (
	"context"
	"strconv"
	"strings"
	"time"

	"gitee.com/go-linux/logger"
	"golang.org/x/sync/errgroup"
)

const MaxGoroutine = 10000

func main() {
	logs, err := logger.NewLog(logger.DebugLevel)
	if err != nil {
		panic(err)
	}
	// logs.Debug("第一面:当前正对的这一面!")
	// logs.Debug("第二面:第一面右边的一面!")
	// logs.Debug("第三面:第二面下边的一面!")
	// logs.Debug("第四面:第三面右边的一面!")
	// logs.Debug("第五面:第四面下边的一面!")
	// logs.Debug("第六面:第五面右边的一面!")
	ctx, cancel := context.WithCancel(context.Background())
	var exit = make(chan []Record)
	defer close(exit)
	var info = new(Info)
	info.logs = logs
	group, ctx := errgroup.WithContext(ctx)
	group.SetLimit(MaxGoroutine)
	info.list = make(map[SerialNo]Position)
	info.group = group
	info.exit = exit
	info.ctx = ctx
	info.cancel = cancel
	info.record = make([]Record, 0)
	group.Go(func() error {
		record := <-exit
		info.output(record, true)
		return nil
	})
	info.init()
	info.logic()
	_ = group.Wait()
}

func (f *Info) output(list []Record, end bool) {
	var record = make([]string, 0)
	var temp = new(Record)
	if end {
		for _, v := range list {
			if temp.Wall == Null || temp.Turn != v.Turn {
				temp.Wall = v.Wall
				temp.Turn = v.Turn
				continue
			}
			//本次又复原到上次了
			if temp.Wall == v.Wall && temp.Turn != v.Turn {
				temp.Wall = Null
				temp.Turn = Null
				continue
			}
			record = append(record, "第"+strconv.FormatInt(int64(temp.Wall), 10)+"面"+TurnString[temp.Turn])
			temp.Wall = v.Wall
			temp.Turn = v.Turn
		}
		if temp.Wall != Null && temp.Turn != Null {
			record = append(record, "第"+strconv.FormatInt(int64(temp.Wall), 10)+"面"+TurnString[temp.Turn])
		}
		f.logs.Info(strings.Join(record, ","))
		return
	}
	for _, v := range list {
		record = append(record, "第"+strconv.FormatInt(int64(v.Wall), 10)+"面"+TurnString[v.Turn])
	}
	f.logs.Debug(strings.Join(record, ","))
}

func (f *Info) init() {
	//todo 初始化数据
	f.list[SerialNo{Wall: One, Block: One}] = Position{X: 1, Y: 3, Z: 3}
	f.list[SerialNo{Wall: One, Block: Two}] = Position{X: 2, Y: 3, Z: 3}
	f.list[SerialNo{Wall: One, Block: Three}] = Position{X: 3, Y: 3, Z: 3}
	f.list[SerialNo{Wall: One, Block: Four}] = Position{X: 3, Y: 2, Z: 3}
	f.list[SerialNo{Wall: One, Block: Five}] = Position{X: 2, Y: 1, Z: 2}
	f.list[SerialNo{Wall: One, Block: Six}] = Position{X: 3, Y: 1, Z: 2}
	f.list[SerialNo{Wall: One, Block: Seven}] = Position{X: 1, Y: 1, Z: 1}
	f.list[SerialNo{Wall: One, Block: Eight}] = Position{X: 2, Y: 1, Z: 1}
	f.list[SerialNo{Wall: One, Block: Nine}] = Position{X: 3, Y: 1, Z: 1}
	f.list[SerialNo{Wall: Two, Block: One}] = Position{X: 3, Y: 3, Z: 3}
	f.list[SerialNo{Wall: Two, Block: Two}] = Position{X: 2, Y: 1, Z: 3}
	f.list[SerialNo{Wall: Two, Block: Three}] = Position{X: 3, Y: 1, Z: 3}
	f.list[SerialNo{Wall: Two, Block: Four}] = Position{X: 3, Y: 1, Z: 2}
	f.list[SerialNo{Wall: Two, Block: Five}] = Position{X: 3, Y: 2, Z: 2}
	f.list[SerialNo{Wall: Two, Block: Six}] = Position{X: 3, Y: 3, Z: 2}
	f.list[SerialNo{Wall: Two, Block: Seven}] = Position{X: 3, Y: 1, Z: 1}
	f.list[SerialNo{Wall: Two, Block: Eight}] = Position{X: 3, Y: 2, Z: 1}
	f.list[SerialNo{Wall: Two, Block: Nine}] = Position{X: 3, Y: 3, Z: 1}
	f.list[SerialNo{Wall: Three, Block: One}] = Position{X: 3, Y: 1, Z: 1}
	f.list[SerialNo{Wall: Three, Block: Two}] = Position{X: 3, Y: 2, Z: 1}
	f.list[SerialNo{Wall: Three, Block: Three}] = Position{X: 3, Y: 3, Z: 1}
	f.list[SerialNo{Wall: Three, Block: Four}] = Position{X: 2, Y: 1, Z: 1}
	f.list[SerialNo{Wall: Three, Block: Five}] = Position{X: 2, Y: 2, Z: 1}
	f.list[SerialNo{Wall: Three, Block: Six}] = Position{X: 2, Y: 3, Z: 1}
	f.list[SerialNo{Wall: Three, Block: Seven}] = Position{X: 1, Y: 1, Z: 1}
	f.list[SerialNo{Wall: Three, Block: Eight}] = Position{X: 1, Y: 2, Z: 1}
	f.list[SerialNo{Wall: Three, Block: Nine}] = Position{X: 1, Y: 3, Z: 1}
	f.list[SerialNo{Wall: Four, Block: One}] = Position{X: 3, Y: 3, Z: 1}
	f.list[SerialNo{Wall: Four, Block: Two}] = Position{X: 3, Y: 3, Z: 2}
	f.list[SerialNo{Wall: Four, Block: Three}] = Position{X: 3, Y: 1, Z: 3}
	f.list[SerialNo{Wall: Four, Block: Four}] = Position{X: 2, Y: 3, Z: 1}
	f.list[SerialNo{Wall: Four, Block: Five}] = Position{X: 2, Y: 3, Z: 2}
	f.list[SerialNo{Wall: Four, Block: Six}] = Position{X: 1, Y: 2, Z: 3}
	f.list[SerialNo{Wall: Four, Block: Seven}] = Position{X: 1, Y: 3, Z: 1}
	f.list[SerialNo{Wall: Four, Block: Eight}] = Position{X: 1, Y: 3, Z: 2}
	f.list[SerialNo{Wall: Four, Block: Nine}] = Position{X: 1, Y: 1, Z: 3}
	f.list[SerialNo{Wall: Five, Block: One}] = Position{X: 1, Y: 3, Z: 1}
	f.list[SerialNo{Wall: Five, Block: Two}] = Position{X: 1, Y: 3, Z: 2}
	f.list[SerialNo{Wall: Five, Block: Three}] = Position{X: 1, Y: 1, Z: 3}
	f.list[SerialNo{Wall: Five, Block: Four}] = Position{X: 1, Y: 2, Z: 1}
	f.list[SerialNo{Wall: Five, Block: Five}] = Position{X: 1, Y: 2, Z: 2}
	f.list[SerialNo{Wall: Five, Block: Six}] = Position{X: 1, Y: 1, Z: 2}
	f.list[SerialNo{Wall: Five, Block: Seven}] = Position{X: 1, Y: 1, Z: 1}
	f.list[SerialNo{Wall: Five, Block: Eight}] = Position{X: 3, Y: 2, Z: 3}
	f.list[SerialNo{Wall: Five, Block: Nine}] = Position{X: 1, Y: 3, Z: 3}
	f.list[SerialNo{Wall: Six, Block: One}] = Position{X: 1, Y: 1, Z: 3}
	f.list[SerialNo{Wall: Six, Block: Two}] = Position{X: 1, Y: 2, Z: 3}
	f.list[SerialNo{Wall: Six, Block: Three}] = Position{X: 3, Y: 1, Z: 3}
	f.list[SerialNo{Wall: Six, Block: Four}] = Position{X: 1, Y: 1, Z: 2}
	f.list[SerialNo{Wall: Six, Block: Five}] = Position{X: 2, Y: 2, Z: 3}
	f.list[SerialNo{Wall: Six, Block: Six}] = Position{X: 2, Y: 1, Z: 3}
	f.list[SerialNo{Wall: Six, Block: Seven}] = Position{X: 1, Y: 3, Z: 3}
	f.list[SerialNo{Wall: Six, Block: Eight}] = Position{X: 2, Y: 3, Z: 3}
	f.list[SerialNo{Wall: Six, Block: Nine}] = Position{X: 3, Y: 3, Z: 3}
}

func (f *Info) logic() {
	select {
	case <-f.ctx.Done():
		return
	default:
		_ = f.Copy().Other(One, true)
		_ = f.Copy().Other(Two, true)
		_ = f.Copy().Other(Three, true)
		_ = f.Copy().Other(Four, true)
		_ = f.Copy().Other(Five, true)
		_ = f.Copy().Other(Six, true)
		_ = f.Copy().Other(One, false)
		_ = f.Copy().Other(Two, false)
		_ = f.Copy().Other(Three, false)
		_ = f.Copy().Other(Four, false)
		_ = f.Copy().Other(Five, false)
		_ = f.Copy().Other(Six, false)
	}
}

func (f *Info) goroutine(fn func() error) {
	ticker := time.NewTicker(time.Microsecond)
	defer ticker.Stop()
	for {
		select {
		case <-f.ctx.Done():
			return
		case <-ticker.C:
			if f.group.TryGo(fn) {
				return
			}
		}
	}
}

func (f *Info) initLogicFunction() map[int]map[bool]func() error {
	var logic = make(map[int]map[bool]func() error)
	logic[One] = make(map[bool]func() error)
	logic[Two] = make(map[bool]func() error)
	logic[Three] = make(map[bool]func() error)
	logic[Four] = make(map[bool]func() error)
	logic[Five] = make(map[bool]func() error)
	logic[Six] = make(map[bool]func() error)
	logic[One][true] = f.Copy().OnePositiveTurn
	logic[Two][true] = f.Copy().TwoPositiveTurn
	logic[Three][true] = f.Copy().ThreePositiveTurn
	logic[Four][true] = f.Copy().FourPositiveTurn
	logic[Five][true] = f.Copy().FivePositiveTurn
	logic[Six][true] = f.Copy().SixPositiveTurn
	logic[One][false] = f.Copy().OneNegativeTurn
	logic[Two][false] = f.Copy().TwoNegativeTurn
	logic[Three][false] = f.Copy().ThreeNegativeTurn
	logic[Four][false] = f.Copy().FourNegativeTurn
	logic[Five][false] = f.Copy().FiveNegativeTurn
	logic[Six][false] = f.Copy().SixNegativeTurn
	return logic
}

func (f *Info) Other(wall int, turn bool) error {
	select {
	case <-f.ctx.Done():
		return nil
	default:
		break
	}
	for w, v := range f.initLogicFunction() {
		for t, fn := range v {
			if wall == w && t == !turn {
				continue
			}
			f.goroutine(fn)
		}
	}
	return f.Copy().Other(wall, turn)
}
